#pragma once

#ifndef RELATIVELAYOUT_H_
#define RELATIVELAYOUT_H_
#include "LayoutGroup.h"

namespace fusion
{
    namespace mode10
    {
		struct  PositionIndexer
		{
		public:
			PositionIndexer(): size(0), isSet(false) {};
			void SetSize(int Size) { size = Size; isSet = true; }
			int GetSize() { return size; }
			bool IsSet() { return isSet; }
			void Clear() { isSet = false; }
		private:
			int size;
			bool isSet;
		};
		
        class RelativeLayout : public LayoutGroup
        {
            typedef unsigned int RuleKey;
        public:
            class RelativeLayoutParams : public MarginLayoutParams
            {
            public:
                static const RuleKey LeftOf;
                static const RuleKey RightOf;
                static const RuleKey Above;
                static const RuleKey Below;

                static const RuleKey AlignLeft;
                static const RuleKey AlignRight;
                static const RuleKey AlignTop;
                static const RuleKey AlignBottom;

                static const RuleKey AlignParentLeft;
                static const RuleKey AlignParentRight;
                static const RuleKey AlignParentTop;
                static const RuleKey AlignParentBottom;

				static const RuleKey CenterInParent;
				static const RuleKey CenterParentHorizontal;
				static const RuleKey CenterParentVertical;
            
                static const unsigned int ParamsLength = 15;
            public:
                RelativeLayoutParams(int width, int height);
                RelativeLayoutParams(const RelativeLayoutParams& params);

                void AddRule(RuleKey ruleKey, int ruleValue);
                int GetRule(RuleKey ruleKey) const;
				
                // TODO hide them
                PositionIndexer mLeft;
                PositionIndexer mRight;
                PositionIndexer mTop;
                PositionIndexer mBottom;
                /*
                 * When true, uses the parent as the anchor if the anchor doesn't exist or
                 * if the anchor's visibility is false
                 */
                bool mAlignWithParent;
            private:
                void InitiateRules();
				RuleKey m_rules[ParamsLength];
            };
        public:
            RelativeLayout(Node* node, Ref<BaseLayoutParams> params);
            ~RelativeLayout();
			virtual void logMeasureResult();
        protected:
            virtual void onMeasure(const Ref<MeasureSpec>& widthSpec, const Ref<MeasureSpec>& heightSpec);
			virtual void onLayout() {};
            virtual Ref<BaseLayoutParams> onGenerateLayoutParams(const Ref<ClientParamsAdapter>& params);
        private:

            /*
             * private classes
             */
            template <typename Type>
            class Create_List
            {
            private:
                List<Type> m_list;
            public:
                Create_List(const Type& item) {
                    m_list.push_back(item);
                }

                Create_List<Type>& operator()(const Type& item)
                {
                    m_list.push_back(item);
                    return *this;
                }

                // XXX Conversion Operators
                operator List<Type>()
                {
                    return m_list;
                }
            };
            /**
             * A node in the dependency graph. A Node is a View, its list of dependencies
             * and its list of dependents.
             *
             * A node with no dependent is considered a root of the graph.
             */
            class NodeDepend : public Referenced
            {
            public:
                explicit NodeDepend(Ref<LayoutNode> node);
                Ref<LayoutNode> GetLayoutNode() const
                {
                    return m_node;
                }

                List< Ref<NodeDepend> > BeDependentsOn;
                Map<RuleKey, Ref<NodeDepend> > DependentsOn;
            private:
                Ref<LayoutNode> m_node; // The node in the layout
                NodeDepend();
            };

            // description how nodes relative to each other
            class DependencyGraph
            {
            public:
                void Clear();
                void Add(Ref<LayoutNode> node);
                void GetSortedView(List<Ref<LayoutNode> >& sorted, const List<RuleKey>& rules);
                List< Ref<NodeDepend> > FindRoots(const List<RuleKey>& ruleFilter);
                Ref<NodeDepend> FindKeyNode(int nodeId);
            private:
                // for debug
                void log(List<RuleKey> rules);
                void printNode(Ref<NodeDepend> node);
                void printNodeName(Ref<NodeDepend> node);
            private:
                List< Ref<NodeDepend> > m_Nodes; // all nodes in the graph
                typedef Map<unsigned int, Ref<NodeDepend> > KeyNode;
                KeyNode m_KeyNodes;
            };

            /*
             * private functions
             */
			//private:
            void SortNodes();

			// horizontal
            void applyHorizontalSizeRules(Ref<RelativeLayoutParams>& params, const Ref<MeasureSpec>& widthSpec);
			void measureChildHorizontal(Ref<LayoutNode>& node, Ref<RelativeLayoutParams>& params, int myWidth, int myHeight);
			void positionChildHorizontal(Ref<LayoutNode>& node, Ref<RelativeLayoutParams>& params, int myWidth);
			void centerHorizontal(Ref<LayoutNode>& node, Ref<RelativeLayoutParams>& params, int myWidth);

			// vertical
			void applyVerticalSizeRules(Ref<RelativeLayoutParams>& params, const Ref<MeasureSpec>& widthSpec);
			void measureChildVertical(Ref<LayoutNode>& node, Ref<RelativeLayoutParams>& params, int myWidth, int myHeight);
			void positionChildVertical(Ref<LayoutNode>& node, Ref<RelativeLayoutParams>& params, int myHeight);
			void centerVertical(Ref<LayoutNode>& node, Ref<RelativeLayoutParams>& params, int myHeight);

            Ref<MeasureSpec> getChildMeasureSpec(PositionIndexer& start, PositionIndexer& end, int childSize, int startMargin, int endMargin, int parentWidth);
			Ref<RelativeLayout::RelativeLayoutParams> getRelatedViewParams(Ref<RelativeLayoutParams> params, RuleKey ruleKey);
            Ref<LayoutNode> getRelatedView(Ref<RelativeLayoutParams> params, RuleKey ruleKey);
            /*
             * private members
             */
            static const List<RuleKey> sRulesVertical;
            static const List<RuleKey> sRulesHorzontal;
            DependencyGraph* m_graph;
            List<Ref<LayoutNode> > m_sortedHorizontalChildren;
            List<Ref<LayoutNode> > m_sortedVerticalChildren;
        };
    }
}


#endif /* RELATIVELAYOUT_H_ */
